chore(electron): 34 → 44 (Chromium 132 → 152, Node 20 → 24) - #148
Merged
Conversation
Electron 34 is ten majors behind and long past end of life: support covers the latest three majors, so 34.5.8 is the final release of that line and will never be patched again. This carries ~20 Chromium majors of unfixed CVEs in an app that renders HTML figures and fetches example data over the network. Chromium 132 -> 152, Node 20.19 -> 24.18. Pinned EXACTLY in BOTH package.json files, which must agree -- see d70f4fb. The workspace hoists electron out of electron/node_modules, so electron-builder cannot read the installed version, falls back to parsing the spec, and refuses a range. That failure is release-only. Only two API breaks across ten majors, both caught by typecheck: - printToPDF's `marginType` is gone. The 'default' it named is now what you get by omitting `margins` entirely (1cm a side), so the fix is a deletion. - clipboard.writeImage is gone; the module is rearchitected to the W3C shape (MIME-typed ClipboardItems, async). The nativeImage decode stays as the validity check that keeps junk off the user's clipboard. report_export.spec.ts asserted the OS clipboard through readImage(), removed in the same change, so it now asks whether the clipboard holds a non-empty image/png. CI gains an explicit `npx install-electron`: Electron 42 dropped the postinstall binary download, so `npm ci` leaves node_modules/electron/dist empty and the ~100 MB fetch happens lazily on the first require('electron') -- which in CI is inside the first spec that launches the app, where a slow or failed download reads as a mystery test timeout rather than an install failure. Swept the rest of the 35-44 breaking changes against this codebase: Unity and Linux badge/progress APIs, pre-macOS-13 login item attributes, win32-ia32 and linux-armv7l targets, renderer-side clipboard, ANGLE, webRequest filters, setPreloads, webFrame.routingId, desktopCapturer, session extension APIs. None are used here. console-message keeps working -- the WebContents overload still emits the positional arguments, deprecated but present -- but `level` was remapped, so the comment describing it was corrected: 0=verbose 1=info 2=warning 3=error, which makes `level >= 2` warnings-and-errors rather than errors-and-infos. e2e: 422 passed / 12 failed / 22 skipped over the full suite. Two of those failures were real and are fixed here (the clipboard assertion above, plus a stale local Playwright browser cache). The remaining ten reproduce on a quiet machine and are being checked against main before any claim about them.
Under Electron 44 the three cells came back as ['Beta', empty, empty] -- deterministically, 3 runs out of 3, while passing on 34. Not an app bug. Every step of the helper addresses "the last cell", so each is wrong the moment it runs early: a dblclick before the new cell mounts opens the PREVIOUS cell's editor, and returning before the commit renders lets the next call race it. A probe driving the same gestures with waits passes on 44, and the screenshot shows Alpha/Beta/Gamma committed correctly -- the feature works, the assumption that a click had landed by the next line did not. Chromium 152 just changed the timing enough to stop the assumption holding.
CI on Linux failed 29 e2e tests under Electron 44 that pass on 34, almost all
of them a caret click timing out with
<iframe ...> from <div data-testid="subwindow"> subtree intercepts pointer
events
The screenshot shows why: a subwindow is painted over the open wizard, clipping
its "+ Component" button, so the click lands on the window's figure iframe.
This is OUR bug, not Electron's. MDI windows take `10 + focusOrderIndex`, which
grows with the number of windows; WizardShell took a fixed 14. At five windows
a focused one ties the wizard and wins. The fit workflow opens navigator,
signal, "Fit components (live)", "chi squared" and the component maps, so it
crosses that line routinely. Electron 44 did not change stacking -- it changed
window count/ordering/timing enough to cross it. Anyone with five windows open
could already bury a caret under one.
So state the layering instead of leaving it to chance: windows own
1..MDI_Z_CEILING (899, clamped), and everything that must float above them --
wizard shells, floating toolbars, caret popovers -- starts at CHROME_Z (1000),
keeping the relative order it already had.
Also two test-side fixes for the same CI run:
- backendErrorLines ignored "Failed to create WebGPU Context Provider" but
Chromium 152 says "No available adapters" for the same missing-GPU
condition, so three "no tracebacks" audits started failing on a headless
runner having no GPU. Match both wordings; the comment already warned that
an Electron bump must not turn infrastructure noise into a fake backend
error, and this is that warning coming true a second time.
- report_reorder built its markdown cells with the same unwaited
click -> dblclick -> fill -> commit sequence already fixed in
report_delete_undo (1dbc143).
Verified locally: fit_wizard, action_scoping, mdi_overlay, report_reorder and
report_delete_undo all pass. The failure itself only reproduces on Linux, so CI
is the real check.
The previous commit raised the caret's own z-index and did nothing, because a caret cannot reach above its window from inside it. `FloatingToolbar` renders inside `SubWindow`, whose root is positioned with a z-index and therefore forms a stacking context; everything within is confined to that window's level. Measured, once the overlap is forced locally: caret z-index 1002 at (428,424) window z-index 11 covers that point elementFromPoint -> IFRAME:figure-56310c3e 1002 losing to 11 is only possible inside a trapped subtree. So keep the OWNING window on top instead: a window with an open caret takes MDI_Z_CEILING, which is the one thing that actually keeps the caret reachable. This is what reddened ~25 e2e tests on the Electron 44 branch. Fit, DPC, strain and orientation all open result windows AFTER their caret; each new window took focus, got a higher z-index, covered the caret it was launched from, and its figure iframe swallowed the clicks -- "iframe ... intercepts pointer events". Nothing about it is Electron-specific: the upgrade only changed which windows landed where. Any user who opened a result window over an open caret already had an unclickable panel. caret_above_windows.spec.ts pins the contract and reproduces the bug on any platform by forcing the overlap rather than hoping for it. It asserts through elementFromPoint, which is the question a click actually asks, and guards the setup: if no window ends up covering the control, the spec FAILS rather than passing without testing anything (it caught exactly that while being written -- grabbing a titlebar's centre starts an HTML5 drag instead of moving the window). Local sweep: caret_above_windows, fit_wizard, action_scoping, mdi_layout and mdi_overlay all pass. ui_fixes "6: Rebin" still fails here, as it does on main with Electron 34 on this machine -- environmental, not from this change.
ipf_perf carried its own copy of backendErrorLines. The copy knew only the parenthesised `bus.cc(406)` stderr shape and the old "Failed to create WebGPU Context Provider" wording, so under Chromium 152 -- which writes `dbus/bus.cc:406]` and "No available adapters" -- ordinary headless-CI noise came back as "backend errors during IPF render". backendErrorLines now takes either the backend or a plain array of lines, so a spec holding its own log snapshot has no reason to fork the filter. That is the whole point: the shared one already had both wordings, and this spec failed only because it had drifted.
Reverts the z-order pin from f10c27e and fixes this where it actually belongs. The pin kept a window with an open caret on top, which changed behaviour nobody asked to change: a newly opened result window should come to the front over the caret, and the caret should stay open underneath. That was already right. What was wrong is PLACEMENT. findFreeSlot packs a new window into the first slot colliding with no existing WINDOW, and knew nothing about carets — so a fit / DPC / strain / orientation run dropped its own result window squarely onto the panel that launched it. Measured with the caret-obstacle line disabled: 99% of the caret buried. With it: 0%. Because a caret lives inside its window's stacking context (SubWindow's root is positioned WITH a z-index), it cannot paint above that new window whatever z-index it takes — a caret at z-index 1002 lost to a window at 11. So the window's figure iframe swallowed every click meant for the caret, which is what reddened ~18 e2e tests on the Electron 34 -> 44 upgrade. FloatingToolbar now reports its open caret's rect in area coords (mirroring the placement its own CSS produces; approximate for the side placements, and erring large is safe), SubWindow relays it, and MDIArea feeds it into the taken list. Z-order is untouched. Also reverts the CHROME_Z / MDI_Z_CEILING constants from 55f471f. They were inert — a child cannot escape its parent's stacking context — and their comments asserted an explanation that is simply false, which is worse than no comment. caret_placement.spec.ts pins the contract and was verified to FAIL without the fix (99% overlap) before being trusted to pass with it.
MDI windows overlap, and a window opened later sits above an earlier one -- over its toolbar, its open caret and its view chips. That is the intended behaviour: a result window should come to the front, and the caret stays open underneath. A person clicks the window they want and carries on, so it never registers as a problem. A spec has no such reflex. It keeps clicking a point that is now behind another window until it times out, reporting "<iframe ...> intercepts pointer events". That is what the remaining Electron 44 e2e failures were -- not a regression, just placement moving enough that specs which had always been one unlucky layout away from this finally hit it. raiseWindow / raiseWindowOwning do what the user does. Both are idempotent (raising the top window changes nothing), so they are safe to add defensively, and raiseWindowOwning is a no-op when nothing matches rather than turning a missing element into a confusing failure elsewhere. Neither closes an open caret: FloatingToolbar's outside-click handler returns early for WIZARD_ACTIONS and ignores clicks inside its own window. All six fit specs failed at the same point -- clicking `fit-tab-Run` after adding components, which opens the live-preview window over the caret -- so that is one uniform insertion, not six bespoke ones. Also applied to ipf_two_window's chip loop, ebsd_workflow's Similarity chip and ui_fixes' Rebin button. NB grep silently returns nothing on fit_handles.spec.ts here (ugrep), which is why its Run-tab click looked absent; awk found it at line 235. Worth knowing before concluding a spec does not contain something.
Two follow-ups to 91b004a, both the same root cause seen from different angles. raiseWindow clicked the titlebar, which has to WIN hit-testing -- and the whole reason for raising is that something is on top. ebsd_workflow found the case where the titlebar ITSELF was covered, so the raise timed out with the very "iframe ... intercepts pointer events" it exists to avoid. SubWindow raises on mousedown on its root, so dispatch there instead: the same handler fires whatever is above it. This is a SETUP step -- whatever the spec does next is still a real hit-tested click, so nothing is papered over. The two 5-D crosshair specs are the same problem wearing a different mask. A drag gets no actionability check, so a navigator under another window does not time out; it just does not move. vectors_dp_follows_nav reported the crosshair moving 0px of a required 5, find_vectors_5d 8px of a required 10 -- which reads like a threshold to tune and is not. Raise before dragging.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Electron 34 is ten majors behind and long past end of life. Support covers the latest three majors, so
34.5.8is the final release of that line and will never be patched again — ~20 Chromium majors of unfixed CVEs in an app that renders HTML figures and fetches example data over the network.Pinned exactly in both
package.jsonfiles, which must agree — see d70f4fb. The workspace hoistselectronout ofelectron/node_modules, so electron-builder can't read the installed version, falls back to parsing the spec, and refuses a range. That failure is release-only and is what killed the v0.4.0 tag build.Application changes: three
printToPDF'smarginTypeis gone. The'default'it named is now what you get by omittingmarginsentirely (1 cm a side), so the fix is a deletion.clipboard.writeImageis gone; the module is rearchitected to the W3C shape — MIME-typedClipboardItems, all async. ThenativeImagedecode stays as the validity check that keeps junk off the user's clipboard.findFreeSlotpacks a window into the first slot colliding with no existing window and knew nothing about carets, so a fit / DPC / strain / orientation run dropped its own result window onto the panel that launched it. Z-order is untouched — a window still comes to the front over a caret it overlaps, and the caret stays open underneath.Everything else across ten majors was checked and found unused here: Unity/Linux badge + progress APIs, pre-macOS-13 login-item attributes,
win32-ia32/linux-armv7l, renderer-sideclipboard, ANGLE,webRequestfilters,setPreloads,webFrame.routingId,desktopCapturer, session extension APIs.console-messagestill works (theWebContentsoverload keeps emitting positional args) butlevelwas remapped, so the comment describing it is corrected:0=verbose 1=info 2=warning 3=error.CI gains one step
Electron 42 dropped the postinstall binary download, so
npm cileavesnode_modules/electron/distempty and the ~100 MB fetch happens lazily on the firstrequire('electron')— in CI, inside the first spec that launches the app, where a slow or failed download reads as a mystery test timeout.npx install-electronmakes it fail loudly on its own line.The e2e work: nothing regressed, the tests lacked a reflex
29 e2e tests went red on Linux. Every one traced to a window covering the control — which is intended behaviour. A person clicks the window and carries on; a spec keeps clicking a covered point until it times out.
raiseWindow— do what the user doesmousedown(SubWindow raises on it) rather than clickbackendErrorLinesTwo worth calling out:
find_vectors_5dreported the marker moving 8px of a required 10. That reads exactly like a threshold to relax — and relaxing it would have buried a drag landing on the wrong window entirely. A drag gets no actionability check, so interception shows up as a short move, not a timeout.Verification
npm run dist:dir→ 348 MBSpyDE.appcarrying Electron Framework 44.0.0. This is the check that does not otherwise run until a tag build — and is how the v0.4.0 release brokemeanDiff: 0, maxDiff: 2between the WebGPU and Canvas2D renders, screenshots compared by eye. Worth having across Chromium 132 → 152caret_placement.spec.tswas verified to FAIL without the fix (99% of the caret buried) before being trusted to pass with itUnrelated, found on the way
spyde/backend/dask_stats.py:113callsclient.run(_trim_process_memory, wait=False), which tripsdistributed2026.3.0'sassert wait or is_coro. Every post-batch worker memory trim fails and logs three tracebacks; only the backend's own local trim runs.is_coroalso satisfies the assert, so anasync defwrapper restores the deliberate fire-and-forget. Not touched here — worth its own PR.